﻿using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using 实时曲线.Models;
using 实时曲线.UControls;

namespace 实时曲线
{
    public partial class Form1 : Form
    {
        SerialPort serialPort; // 串口对象
        List<SlaveInfo> slaves = new List<SlaveInfo>(); // 从站信息列表
        List<ParaInfo> paraInfos = new List<ParaInfo>(); // 存储参数列表
        IModbusMaster master = null; // Modbus主站对象
        System.Timers.Timer timer = null;  // 基于子线程的定时器
        DataInfo curData = null; // 当前采集数据对象

        public Form1()
        {
            InitializeComponent();
            this.FormClosing += Form1_FormClosing;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            timer?.Stop();
            timer?.Dispose();
            timer = null;

            master.Dispose();

            serialPort?.Close();
            serialPort?.Dispose();
        }

        /// <summary>
        /// 重复执行事件处理程序  ---- 采集与加载 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            // 加载从站列表
            LoadSlaveList();
            // 加载参数列表
            LoadParaList();
            // 创建串口对象
            CreateConn();
            timer = new System.Timers.Timer();
            timer.Interval = 1000; // 设置定时器间隔为1000毫秒
            // AutoReset=true, // 定时器每次到达间隔时间后自动触发事件处理程序，会有上一次没执行完就开始下一次的情况。
            timer.AutoReset = false; // 如果你希望每次任务执行完再开始下一次，设置 AutoReset = false，默认值为true
            timer.Elapsed += Timer_Elapsed;
            curData = new DataInfo(); // 初始化当前数据对象
        }

        /// <summary>
        /// 重复执行事件处理程序  ---- 采集与加载 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private async void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            // 如果timer已被释放,直接返回
            if (timer == null) return;

            // 子线程的定时器采集数据
            await ReadAndLoad();

            // 重启定时器
            //如果你希望每次任务执行完再开始下一次，建议设置 AutoReset = false，并在任务完成后手动调用 timer.Enabled = true。
            if (timer != null)
            {
                timer.Enabled = true; // 重新启用定时器
            }
        }

        private async Task ReadAndLoad()
        {
            // 读取
            foreach (SlaveInfo slave in slaves)
            {
                // 根据从站获取对应的参数信息
                var paras = paraInfos.Where(p => p.SlaveId == slave.SlaveId).ToList();
                if (slave.FunctionCode == 1)  // 输出线圈
                {
                    // 读取从站中寄存器的数据 （1-1 从站1,功能码1）（2-3 从站2,功能码2）
                    bool[] bls = await master.ReadCoilsAsync(slave.SlaveId, slave.StartAddress, slave.Count);
                    // 解析--->把数据存储到对应的参数中
                    foreach (var para in paras)
                    {
                        // 寄存器地址
                        ushort addr = para.Address;
                        // 取对应的值
                        bool blVal = bls[addr];

                        // 获取参数名
                        string paraName = para.ParaName;
                        // 通过反射给curData赋值
                        SetParaValue<bool>(paraName, blVal);
                    }
                }
                else if (slave.FunctionCode == 3)  // 保持寄存器
                {
                    // 读取
                    ushort[] uDatas = await master.ReadHoldingRegistersAsync(slave.SlaveId, slave.StartAddress, slave.Count);

                    List<TrendCacheInfo> curCacheList = new List<TrendCacheInfo>();

                    // 解析--->把数据存储到对应的参数中
                    foreach (var para in paras)
                    {
                        // 寄存器地址
                        ushort addr = para.Address;
                        // 取对应的值
                        ushort data = uDatas[addr];
                        // 获取参数名
                        string paraName = para.ParaName;
                        string strVal = "";
                        if (para.DataType == "Decimal")
                        {
                            // 带小数
                            decimal val = (decimal)data / 10;
                            decimal newVal = decimal.Parse(val.ToString("0.0")); // 保留一位小数
                            // 通过反射给curData赋值
                            SetParaValue<decimal>(paraName, newVal);
                            strVal = newVal.ToString();
                        }
                        else
                        {
                            SetParaValue<ushort>(paraName, data);
                            strVal = data.ToString();
                        }

                        // 添加到当前曲线缓存列表
                        TrendCacheInfo curCache = new TrendCacheInfo();
                        curCache.InsertTime = DateTime.Now;
                        curCache.ParaName = paraName;
                        curCache.ParaValue = strVal;
                        curCacheList.Add(curCache);
                    }

                    // 添加到当前曲线缓存总缓存列表List<ist<TrendCacheInfo>>中
                    if (CommonClass.trendCacheInfos.Count > CommonClass.trendCacheCount)
                    {
                        // 超过容量，移除最早的缓存
                        CommonClass.trendCacheInfos.RemoveAt(0);
                    }
                    CommonClass.trendCacheInfos.Add(curCacheList);

                }

            }


            // 加载( 在 Invoke 前，双重检查窗体是否已被释放!this.IsDisposed)
            if (this.IsHandleCreated && !this.IsDisposed)
            {
                this.Invoke(new Action(() =>
                {
                    // 遍历空间的方式
                    foreach (Control c in this.Controls)
                    {
                        if (c is ParaTextBox)
                        {
                            ParaTextBox p = c as ParaTextBox;
                            string pName = p.VarName;
                            // 赋值
                            p.DataVal = GetParaValue<string>(pName);
                        }

                        if (c is UPump)
                        {
                            UPump uPump = c as UPump;
                            string pName = uPump.PumpStateName;
                            // 赋值
                            uPump.ActualState = GetParaValue<bool>(pName);
                        }

                        if (c is GroupBox)
                        {
                            foreach (Control cc in c.Controls)
                            {
                                if (cc is ParaTextBox p)
                                {
                                    string pName = p.VarName;
                                    // 反射的方式取值
                                    p.DataVal = GetParaValue<string>(pName);
                                }
                            }
                        }

                    }
                }));
            }
            else
            {
                // 窗体已释放，停止定时器
                timer?.Stop();
            }
        }

        private void SetParaValue<T>(string paraName, T blVal)
        {
            typeof(DataInfo)?.GetProperty(paraName)?.SetValue(curData, blVal);
        }

        private T GetParaValue<T>(string paraName)
        {
            return (T)Convert.ChangeType(typeof(DataInfo)?.GetProperty(paraName)?.GetValue(curData), typeof(T));
        }

        /// <summary>
        /// 加载参数列表
        /// </summary>
        private void LoadSlaveList()
        {
            XmlDocument doc = new XmlDocument();
            string path = Application.StartupPath + "/configFiles/XSlave.xml";
            doc.Load(path);
            // 获取根节点
            XmlElement rootNode = doc.DocumentElement;

            if (rootNode != null)
            {
                foreach (XmlNode node in rootNode.ChildNodes)
                {
                    SlaveInfo slave = new SlaveInfo();
                    slave.SlaveId = byte.Parse(node.SelectSingleNode("SlaveId").InnerText);
                    slave.FunctionCode = byte.Parse(node.SelectSingleNode("FunctionCode").InnerText);
                    slave.StartAddress = ushort.Parse(node.SelectSingleNode("StartAddress").InnerText);
                    slave.Count = ushort.Parse(node.SelectSingleNode("Count").InnerText);
                    slaves.Add(slave);
                }
            }
        }

        /// <summary>
        /// 加载从站列表
        /// </summary>
        private void LoadParaList()
        {
            XmlDocument doc = new XmlDocument();
            string path = Application.StartupPath + "/configfiles/XParas.xml";
            doc.Load(path);
            // 获取根节点
            XmlElement rootElement = doc.DocumentElement;
            if (rootElement != null)
            {
                foreach (XmlNode node in rootElement.ChildNodes)
                {
                    ParaInfo para = new ParaInfo();
                    // 从节点获取参数信息
                    para.ParaName = node.SelectSingleNode("ParaName").InnerText;
                    // 从站地址
                    para.SlaveId = byte.Parse(node.SelectSingleNode("SlaveId").InnerText);
                    // 寄存器地址
                    para.Address = ushort.Parse(node.SelectSingleNode("Address").InnerText);
                    // 数据类型
                    para.DataType = node.SelectSingleNode("DataType").InnerText;
                    // 批注
                    para.Note = node.SelectSingleNode("Note").InnerText;

                    paraInfos.Add(para);

                    if (para.DataType != "Bool")
                    {
                        CommonClass.trendParaInfos.Add((para.ParaName, para.Note));
                    }
                }
            }
        }

        /// <summary>
        /// 创建串口对象
        /// </summary>
        private void CreateConn()
        {
            serialPort = new SerialPort("COM19", 9600, Parity.None, 8, StopBits.One);
            if (serialPort != null)
            {
                try
                {
                    // 打开串口连接
                    serialPort.Open();
                    if (serialPort.IsOpen)
                    {
                        master = ModbusSerialMaster.CreateRtu(serialPort);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }

            }
        }

        bool isStart = false;
        private async void btnStart_Click(object sender, EventArgs e)
        {
            if (!isStart)
            {
                // 启动
                await Start(true);
                isStart = true;
                timer.Start();
                lblState.Text = "已启动";
                btnStart.Text = "停止";
            }
            else
            {
                // 停止
                await Start(false);
                isStart = false;
                timer.Stop();
                uPump1.ActualState = false; // 停止时泵的状态为关闭
                uPump2.ActualState = false; // 停止时泵的状态为关闭
                lblState.Text = "已停止";
                btnStart.Text = "启动";
            }
        }

        private async Task Start(bool bl)
        {
            SlaveInfo slave = slaves.Find(s => s.SlaveId == 1);
            await master.WriteMultipleCoilsAsync(slave.SlaveId, slave.StartAddress, new bool[] { bl, bl });
        }

        private void btnLiveTrend_Click(object sender, EventArgs e)
        {
            FrmTrend frmTrend = new FrmTrend();
            frmTrend.Show();
        }
    }
}
